﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Windows.Media.Animation;
using System.Windows.Threading;

namespace SARControlLib
{
    /// <summary>
    /// SARPDisplay.xaml 的交互逻辑
    /// </summary>
    public partial class SARFanDisplay : UserControl
    {
        #region 普通的私有变量
        /// <summary>
        /// 当前控件的实际宽度
        /// </summary>
        private double ctrlWidth;
        /// <summary>
        /// 当前控件的实际高度
        /// </summary>
        private double ctrlHeight;

        /// <summary>
        /// 扇形的圆心
        /// </summary>
        private Point center;

        /// <summary>
        /// 最外层圆的半径
        /// </summary>
        private double R1;
        /// <summary>
        /// 第二个圆的半径
        /// </summary>
        private double R2;
        /// <summary>
        /// 第三个圆的半径
        /// </summary>
        private double R3;
        /// <summary>
        /// 第四个圆的半径
        /// </summary>
        private double R4;
        /// <summary>
        /// 第五个圆的半径
        /// </summary>
        private double R5;
        /// <summary>
        /// 第六个圆的半径
        /// </summary>
        private double R6;
        /// <summary>
        /// 中间环的半径
        /// </summary>
        private double DR1;
        /// <summary>
        /// 内环的半径
        /// </summary>
        private double DR2;

        /// <summary>
        /// 波束对象
        /// </summary>
        private Path beam;
        /// <summary>
        /// 控制波束旋转的对象
        /// </summary>
        private RotateTransform beamTrans;

        /// <summary>
        /// 每一个环的宽度
        /// </summary>
        private double ringWidth = 8;

        /// <summary>
        /// 外面的虚线环
        /// </summary>
        Path dashCircle1;
        /// <summary>
        /// 里面的虚线环
        /// </summary>
        Path dashCircle2;

        private int mark1LongNum = 12;
        private double mark1LongLength = 8;
        private int mark1ShortNum = 120;
        private double mark1ShortLength = 4;

        private int mark2LongNum = 4;
        private double mark2LongLength = 12;
        private int mark2ShortNum = 12;
        private double mark2ShortLength = 8;

        private int mark3LongNum = 4;
        private double mark3LongLength = 12;
        private int mark3ShortNum = 12;
        private double mark3ShortLength = 8;

        private int mark1and2Num = 4;
        private int mark1and2OutSpace = 10;
        private int mark1and2InSpace = 20;
        private int mark2and3Num = 4;
        private int mark2and3OutSpace = 10;
        private int mark2and3InSpace = 20;

        /// <summary>
        /// 线条的画刷
        /// </summary>
        private Brush lineBrush = new SolidColorBrush(Color.FromArgb(0xff, 0x78, 0x54, 0x0c));

        /// <summary>
        /// 填充的画刷
        /// </summary>
        private Brush fillBrush = new SolidColorBrush(Color.FromArgb(0xff, 0xe5, 0xae, 0x1e));

        /// <summary>
        /// 波束填充的颜色
        /// </summary>
        private Color beamColor;

        private double textSpace = 23;

        #endregion

        #region 需要封装为属性的的私有变量
        double scanStartAngle = -60;
        /// <summary>
        /// 扫描的起始角度
        /// </summary>
        public double SARScanStartAngle
        {
            get { return scanStartAngle; }
            set { scanStartAngle = value; }
        }


        double scanEndAngle = 60;
        /// <summary>
        /// 扫描的结束角度
        /// </summary>
        public double SARScanEndAngle
        {
            get { return scanEndAngle; }
            set { scanEndAngle = value; }
        }

        private int segmentNum1 = 12;
        /// <summary>
        /// 外面虚线环的分段个数
        /// </summary>
        public int SARSegmentNum1
        {
            get { return segmentNum1; }
            set
            {
                segmentNum1 = value;
                getDashFan(dashCircle1, center.X, center.Y, scanStartAngle, scanEndAngle, DR1, ringWidth, segmentNum1, fillBrush);
            }
        }

        /// <summary>
        /// 内环的分段数
        /// </summary>
        private int segmentNum2 = 6;
        /// <summary>
        /// 里面虚线环的分段个数
        /// </summary>
        public int SARSegmentNum2
        {
            get { return segmentNum2; }
            set
            {
                segmentNum2 = value;
                getDashFan(dashCircle2, center.X, center.Y, scanStartAngle, scanEndAngle, DR1, ringWidth, segmentNum1, fillBrush);
            }
        }

        private double textStartAngle = -60;
        /// <summary>
        /// 刻度文本的起始角度
        /// </summary>
        public double SARTextStartAngle
        {
            set
            {
                textStartAngle = value;
                drawText(center.X, center.Y, R1, textStartAngle, textStep, textNum, lineBrush);
            }
            get
            {
                return textStartAngle;
            }
        }

        private int textNum = 13;
        /// <summary>
        /// 文本刻度的个数
        /// </summary>
        public int SARTextNum
        {
            set
            {
                textNum = value;
                drawText(center.X, center.Y, R1, textStartAngle, textStep, textNum, lineBrush);
            }
            get
            {
                return textNum;
            }
        }

        private double textStep = 10;
        /// <summary>
        /// 文本刻度的步长
        /// </summary>
        public double SARTextStep
        {
            get { return textStep; }
            set { textStep = value; }
        }

        private double sarBeamAngel = 30;
        /// <summary>
        /// 余晖宽度
        /// </summary>
        public double SARBeamAngle
        {
            get { return sarBeamAngel; }
            set
            {
                sarBeamAngel = value;
                getBeam(beam, center.X, center.Y, R2, beamColor);
                beamTrans.Angle = currentDirect;
            }
        }

        /// <summary>
        /// 波束指向
        /// </summary>
        private double currentDirect = 0;
        /// <summary>
        /// 波束指向
        /// </summary>
        public double SARBeamDirect
        {
            get { return currentDirect; }
            set
            {
                currentDirect = value;
                beamTrans.Angle = currentDirect;
            }
        }

        private double beamLight = 0.8;

        /// <summary>
        /// 波束亮度
        /// </summary>
        public double SARBeamLight
        {
            get { return beamLight; }
            set
            {
                beamLight = value;
                beamColor.A = (byte)(0xff * value);
                getBeam(beam, center.X, center.Y, R2, beamColor);
            }
        }

        /// <summary>
        /// 背景透明度
        /// </summary>
        public double SARBackImageOpacity
        {
            set
            {
                this.background.Opacity = value;
            }
            get
            {
                return this.background.Opacity;
            }
        }

        private double scale = 100;
        /// <summary>
        /// 比例尺，也就是最外层圈半径代表的距离,单位为m
        /// </summary>
        public double SARScale
        {
            get { return scale; }
            set { scale = value; }
        }

        private List<SARTarget> targets = new List<SARTarget>();

        /// <summary>
        /// 添加一个目标
        /// </summary>
        /// <param name="t"></param>
        public void SARAddTarget(SARTarget t)
        {
            Point pos = getTargetPoint(t);
            t.Margin = new Thickness(pos.X - 15, pos.Y - 15, 0, 0);
            t.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
            t.VerticalAlignment = System.Windows.VerticalAlignment.Top;
            targets.Add(t);
            this.targetLayer.Children.Add(t);
        }

        /// <summary>
        /// 删除一个目标
        /// </summary>
        /// <param name="index"></param>
        public void SARRemoveTarget(int index)
        {
            SARTarget t = targets[index];
            targets.RemoveAt(index);
            this.targetLayer.Children.Remove(t);
        }

        /// <summary>
        /// 更新目标位置
        /// </summary>
        /// <param name="index"></param>
        public void SARUpdateTargetPosition(int index)
        {
            SARTarget t = targets[index];
            Point pos = getTargetPoint(t);
            t.Margin = new Thickness(pos.X - 15, pos.Y - 15, 0, 0);
        }

        private Point getTargetPoint(SARTarget t)
        {
            double x = center.X + t.SARDistance / scale * R1 * Math.Sin((t.SARAngle+180) / 180 * Math.PI);
            double y = center.Y + t.SARDistance / scale * R1 * Math.Cos((t.SARAngle+180) / 180 * Math.PI);
            Point result = new Point(x, y);
            return result;
        }
        #endregion

        #region 初始化方法
        public SARFanDisplay()
        {
            this.InitializeComponent();
            this.Loaded += new RoutedEventHandler(SARPDisplay_Loaded);
        }

        void SARPDisplay_Loaded(object sender, RoutedEventArgs e)
        {
            SARInitDisplay();
        }

        private double getRadius(double width, double height, double scanStartAngle, double scanEndAngle, double margin)
        {
            double usableWitdh = width - 2 * margin;
            double usebleHeight = height - 2 * margin;

            double biggerAngle = (-scanStartAngle) > scanEndAngle ? (-scanStartAngle) : scanEndAngle;
            double biggerAngle1 = biggerAngle / 180 * Math.PI;
            double fwidth;
            double fheight;

            if (biggerAngle <= 90)
            {
                fheight = 1;
                fwidth = 2 * Math.Sin(biggerAngle1);
            }
            else
            {
                fwidth = 2;
                fheight = 1 + Math.Sin(biggerAngle1 - Math.PI / 2);
            }

            //左右两边顶齐
            if (fwidth / fheight > usableWitdh / usebleHeight)
            {
                return usableWitdh / fwidth;
            }
            //上下顶起
            else
            {
                return usebleHeight / fheight;
            }

        }

        /// <summary>
        /// 获取扇形的圆心
        /// </summary>
        /// <param name="width">控件的宽度</param>
        /// <param name="height">控件的高度</param>
        /// <param name="scanStartAngle">扫描的起始角度</param>
        /// <param name="scanEndAngle">扫描的结束角度</param>
        /// <param name="margin"></param>
        /// <returns></returns>
        private Point getCenter(double width, double height, double scanStartAngle, double scanEndAngle, double margin, double r)
        {
            double usableWitdh = width - 2 * margin;
            double usebleHeight = height - 2 * margin;

            double biggerAngle = (-scanStartAngle) > scanEndAngle ? (-scanStartAngle) : scanEndAngle;
            double biggerAngle1 = biggerAngle / 180 * Math.PI;

            if (biggerAngle <= 90)
            {
                return new Point(usableWitdh / 2 + margin, usebleHeight + margin);
            }
            else
            {
                return new Point(usableWitdh / 2 + margin, usebleHeight - r * Math.Sin(biggerAngle1 - Math.PI / 2) + margin);
            }
        }

        private void SARInitDisplay()
        {
            //先获得控件的实际长宽
            ctrlHeight = this.ActualHeight;
            ctrlWidth = this.ActualWidth;
            
            //计算最外层圆的半径
            R1 = getRadius(ctrlWidth, ctrlHeight, scanStartAngle, scanEndAngle, mark1LongLength + textSpace);

            //计算扇形的圆心
            center = getCenter(ctrlWidth, ctrlHeight, scanStartAngle, scanEndAngle, mark1LongLength + textSpace, R1);

            //生成最外层的圆
            Path circle1 = getFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, R1, null, lineBrush);
            this.radarLayer.Children.Add(circle1);

            //计算第二个圆的半径
            R2 = R1 - ringWidth;
            //生成第二个圆
            Path circle2 = getFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, R2, null, lineBrush);
            this.radarLayer.Children.Add(circle2);

            //计算环之间的距离
            double dis = (R1 - 3 * ringWidth) / 3;

            //计算第三个圆的半径
            R3 = R2 - dis;
            //生成第三个圆
            Path circle3 = getFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, R3, null, lineBrush);
            this.radarLayer.Children.Add(circle3);

            //计算第四个圆的半径
            R4 = R3 - ringWidth;
            //生成第四个圆
            Path circle4 = getFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, R4, null, lineBrush);
            this.radarLayer.Children.Add(circle4);

            //计算第五个圆的半径
            R5 = R4 - dis;
            //生成第五个圆
            Path circle5 = getFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, R5, null, lineBrush);
            this.radarLayer.Children.Add(circle5);

            //计算第六个圆的半径
            R6 = R5 - ringWidth;
            //生成第六个圆
            Path circle6 = getFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, R6, null, lineBrush);
            this.radarLayer.Children.Add(circle6);

            //计算第一个虚线环的半径
            DR1 = (R3 + R4) / 2;
            dashCircle1 = getDashFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, DR1, ringWidth, segmentNum1, fillBrush);
            this.radarLayer.Children.Add(dashCircle1);

            //计算第二个虚线环的半径
            DR2 = (R5 + R6) / 2;
            dashCircle2 = getDashFan(null, center.X, center.Y, scanStartAngle, scanEndAngle, DR2, ringWidth, segmentNum2, fillBrush);
            this.radarLayer.Children.Add(dashCircle2);

            //生成最外层长刻度
            Path mark1Long = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R1, mark1LongLength, mark1LongNum, true, lineBrush);
            this.radarLayer.Children.Add(mark1Long);

            //生成最外层短刻度
            Path mark1Short = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R1, mark1ShortLength, mark1ShortNum, true, lineBrush);
            this.radarLayer.Children.Add(mark1Short);

            //生成中层长刻度
            Path mark2Long = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R3, mark2LongLength, mark2LongNum, true, lineBrush);
            this.radarLayer.Children.Add(mark2Long);

            //生成中层短刻度
            Path mark2Short = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R3, mark2ShortLength, mark2ShortNum, true, lineBrush);
            this.radarLayer.Children.Add(mark2Short);

            //生成内层长刻度
            Path mark3Long = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R5, mark3LongLength, mark3LongNum, true, lineBrush);
            this.radarLayer.Children.Add(mark3Long);

            //生成内层短刻度
            Path mark3Short = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R5, mark3ShortLength, mark3ShortNum, true, lineBrush);
            this.radarLayer.Children.Add(mark3Short);

            //生成外环与中环之间的刻度线
            double mark1and2Length = dis - mark1and2InSpace - mark1and2OutSpace;
            Path mark1and2 = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R3 + mark1and2InSpace, mark1and2Length, mark1and2Num, true, lineBrush);
            this.radarLayer.Children.Add(mark1and2);

            //生成中环与内环之间的刻度线
            double mark2and3Length = dis - mark2and3InSpace - mark2and3OutSpace;
            Path mark2and3 = getMark(center.X, center.Y, scanStartAngle, scanEndAngle, R5 + mark2and3InSpace, mark2and3Length, mark2and3Num, true, lineBrush);
            this.radarLayer.Children.Add(mark2and3);

            //生成扫描波束
            beamColor = Color.FromArgb((byte)(0xff * beamLight), 0xe5, 0xae, 0x1e);
            beam = getBeam(null,center.X, center.Y, R2, beamColor);
            this.radarLayer.Children.Add(beam);
            beamTrans = new RotateTransform(currentDirect);
            beamTrans.CenterX = center.X;
            beamTrans.CenterY = center.Y;
            beam.RenderTransform = beamTrans;

            //生成刻度文本
            drawText(center.X, center.Y, R1, textStartAngle, textStep, textNum, lineBrush);

            //DoubleAnimation da = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromMilliseconds(2000)));
            //da.RepeatBehavior = RepeatBehavior.Forever;
            //beamTrans.BeginAnimation(RotateTransform.AngleProperty, da);

            DispatcherTimer tm = new DispatcherTimer();
            tm.Tick += new EventHandler(tm_Tick);
            tm.Interval = TimeSpan.FromSeconds(0.005);
            //tm.Start();

            //new Timer(new TimerCallback(delegate(object o)
            //{
            //    this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (ThreadStart)delegate()
            //    {
            //        this.currentAngle += Math.PI * 100 / 180;
            //        beamTrans.Angle = this.currentAngle;

            //        DateTime now = DateTime.Now;
            //        long timespan = now.Ticks - startTime.Ticks;
            //        count++;

            //        if (timespan > 1000000)
            //        {
            //            txt1.SARText = (count * 1000000.0 / timespan) + "帧每秒";
            //        }
            //    });
            //})).Change(0, 10);
        }

        #endregion

        #region 测试代码
        void tm_Tick(object sender, EventArgs e)
        {
            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (ThreadStart)delegate()
            {
                this.SARBeamDirect += Math.PI * 10 / 180 * dir;
                if (SARBeamDirect >= scanEndAngle || SARBeamDirect <= scanStartAngle)
                {
                    dir = -dir;
                }

                DateTime now = DateTime.Now;
                long timespan = now.Ticks - startTime.Ticks;
                count++;

                if (timespan > 1000000)
                {
                    //txt1.SARText = (count * 1000000.0 / timespan) + "帧每秒";
                }
            });
        }

        int count = 0;
        DateTime startTime = DateTime.Now;
        int dir = 1;

        #endregion

        #region 工厂方法

        /// <summary>
        /// 获得一个扇形
        /// </summary>
        /// <param name="centerX"></param>
        /// <param name="centerY"></param>
        /// <param name="startAngle"></param>
        /// <param name="endAngle"></param>
        /// <param name="radius"></param>
        /// <param name="fillBrush"></param>
        /// <param name="strokeBrush"></param>
        /// <returns></returns>
        private Path getFan(Path oldPath, double centerX, double centerY, double startAngle, double endAngle, double radius, Brush fillBrush, Brush strokeBrush)
        {
            double startAngle1 = startAngle / 180 * Math.PI;
            double endAngle1 = endAngle / 180 * Math.PI;
            Point point1 = new Point(radius * Math.Sin(startAngle1) + centerX, -radius * Math.Cos(startAngle1) + centerY);
            Point point2 = new Point(radius * Math.Sin(endAngle1) + centerX, -radius * Math.Cos(endAngle1) + centerY);

            Path result;
            if (oldPath == null)
            {
                result = new Path();
            }
            else
            {
                result = oldPath;
            }
            PathGeometry fanGeo = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.StartPoint = new Point(centerX, centerY);
            figure.IsClosed = false;

            LineSegment line1 = new LineSegment();
            line1.Point = point1;
            figure.Segments.Add(line1);

            ArcSegment arg = new ArcSegment();
            if (endAngle - startAngle > 180)
            {
                arg.IsLargeArc = true;
            }
            arg.Point = point2;
            arg.Size = new Size(radius, radius);
            arg.SweepDirection = SweepDirection.Clockwise;
            figure.Segments.Add(arg);

            LineSegment line2 = new LineSegment();
            line2.Point = new Point(centerX, centerY);
            figure.Segments.Add(line2);

            fanGeo.Figures.Add(figure);

            result.Data = fanGeo;
            result.Fill = fillBrush;
            result.Stroke = strokeBrush;
            return result;
        }

        private Path getDashFan(Path oldPath, double centerX, double centerY, double startAngle, double endAngle, double radius, double ringWidth, int segmentNum, Brush brush1)
        {
            double startAngle1 = startAngle / 180 * Math.PI;
            double endAngle1 = endAngle / 180 * Math.PI;
            Point point1 = new Point(radius * Math.Sin(startAngle1) + centerX, -radius * Math.Cos(startAngle1) + centerY);
            Point point2 = new Point(radius * Math.Sin(endAngle1) + centerX, -radius * Math.Cos(endAngle1) + centerY);

            Path result;
            if (oldPath == null)
            {
                result = new Path();
            }
            else
            {
                result = oldPath;
            }

            PathGeometry fanGeo = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.StartPoint = point1;
            figure.IsClosed = false;

            ArcSegment arg = new ArcSegment();
            if (endAngle - startAngle > 180)
            {
                arg.IsLargeArc = true;
            }
            arg.Point = point2;
            arg.Size = new Size(radius, radius);
            arg.SweepDirection = SweepDirection.Clockwise;
            figure.Segments.Add(arg);

            fanGeo.Figures.Add(figure);

            result.Data = fanGeo;

            result.StrokeThickness = ringWidth;

            double length = 2 * Math.PI * radius * (endAngle - startAngle) / 360;

            double segmentLen = length / segmentNum;

            result.StrokeDashArray = new DoubleCollection(new double[] { segmentLen / ringWidth, segmentLen / ringWidth });
            result.Stroke = brush1;

            result.StrokeDashOffset = 0;

            return result;
        }       

        private Path getMark(double centerX, double centerY, double startAngle, double endAngle, double radius, double length, int num, bool inout, Brush brush)
        {
            double startAngle1 = startAngle / 180 * Math.PI;
            double angle = (endAngle - startAngle) / 180 * Math.PI / num;

            Path result = new Path();
            GeometryGroup geoGroup = new GeometryGroup();
            result.Data = geoGroup;

            for (int i = 0; i < num; i++)
            {
                Point startPoint = new Point();
                startPoint.X = centerX + radius * Math.Sin(startAngle1 + angle * i);
                startPoint.Y = centerY - radius * Math.Cos(startAngle1 + angle * i);

                Point endPoint = new Point();
                if (inout)
                {
                    endPoint.X = centerX + (radius + length) * Math.Sin(startAngle1 + angle * i);
                    endPoint.Y = centerY - (radius + length) * Math.Cos(startAngle1 + angle * i);
                }
                else
                {
                    endPoint.X = centerX + (radius - length) * Math.Sin(startAngle1 + angle * i);
                    endPoint.Y = centerY - (radius - length) * Math.Cos(startAngle1 + angle * i);
                }

                LineGeometry line = new LineGeometry();
                line.StartPoint = startPoint;
                line.EndPoint = endPoint;
                geoGroup.Children.Add(line);
            }

            result.Stroke = brush;
            result.StrokeThickness = 1;


            return result;
        }

        private Path getBeam(Path oldPath, double centerX, double centerY, double radius, Color color)
        {
            return getFan(oldPath, centerX, centerY, -0.5, 0.5, radius, new SolidColorBrush(color), null);
        }

        private void drawText(double centerX, double centerY, double radius, double startValue, double step, int num, Brush brush)
        {
            this.textLayer.Children.Clear();

            double startValue1 = startValue / 180 * Math.PI;//弧度制的起始角度
            double step1 = step / 180 * Math.PI;//弧度制的角度间隔

            radius = radius + textSpace;

            for (int i = 0; i < num; i++)
            {
                TextBlock txt = new TextBlock();
                txt.Foreground = brush;
                txt.Text = (startValue + i * step).ToString("#0.00");
                Point startPoint = new Point();
                startPoint.X = centerX + radius * Math.Sin(step1 * i + startValue1) - textSpace;
                startPoint.Y = centerY - radius * Math.Cos(step1 * i + startValue1) - textSpace / 2;
                txt.Margin = new Thickness(startPoint.X, startPoint.Y, 0, 0);
                this.textLayer.Children.Add(txt);
            }
        }

        #endregion
    }
}